package generator.mybatis.plugin;

import com.itfsw.mybatis.generator.plugins.utils.JavaElementGeneratorTools;
import org.mybatis.generator.api.FullyQualifiedTable;
import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.Plugin;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

/**
 * 2019/4/23
 * 生成dao层
 *
 * @author machuanpeng
 */
public class MyDaoPlugin extends PluginAdapter {
    /**
     * 返回参数dto包路径
     */
    private String responsePackage;
    /**
     * 包路径
     */
    private String targetPackage;
    /**
     * 目标
     */
    private String targetProject;

    /**
     * 文件名后缀（可自定义Dao、Service。默认Dao）
     */
    private String suffix;

    private IntrospectedTable introspectedTable;

    @Override
    public boolean validate(List<String> warnings) {
        responsePackage = properties.getProperty("responsePackage");
        targetPackage = properties.getProperty("targetPackage");
        targetProject = properties.getProperty("targetProject");
        if (!stringHasValue(targetProject)) {
            targetProject = "mybatis/src/main/java";
        }
        suffix = properties.getProperty("suffix");
        if (!stringHasValue(suffix)) {
            suffix = "Dao";
        }
        return stringHasValue(targetPackage) && stringHasValue(responsePackage);
    }

    /**
     * 添加文件.
     * 2019/6/14
     *
     * @param introspectedTable introspectedTable
     * @return java.util.List<org.mybatis.generator.api.GeneratedJavaFile>
     * @author machuanpeng
     */
    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        List<GeneratedJavaFile> generatedJavaFiles = new ArrayList<>();
        this.introspectedTable = introspectedTable;
        FullyQualifiedTable fullyQualifiedTable = introspectedTable.getFullyQualifiedTable();

        //model名称
        String domainName = fullyQualifiedTable.getDomainObjectName();
        //返回类型
        FullyQualifiedJavaType listType = FullyQualifiedJavaType.getNewListInstance();
        FullyQualifiedJavaType javaType = new FullyQualifiedJavaType(responsePackage + "." + domainName + "ResDTO");
        listType.addTypeArgument(javaType);

        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(javaType);
        importedTypes.add(FullyQualifiedJavaType.getNewListInstance());

        //dao层接口
        FullyQualifiedJavaType interfaceType = new FullyQualifiedJavaType(targetPackage + "." + domainName + suffix);
        Interface interfaze = new Interface(interfaceType);
        interfaze.setVisibility(JavaVisibility.PUBLIC);

        addCountByExampleMethod(interfaze);
        addDeleteByExampleMethod(interfaze);
        addDeleteByPrimaryKeyMethod(interfaze);
        addInsertMethod(interfaze);
        addInsertSelectiveMethod(interfaze);
        addSelectByExampleWithBLOBsMethod(interfaze, listType);
        addSelectByExampleWithoutBLOBsMethod(interfaze, listType);
        addSelectByPrimaryKeyMethod(interfaze, javaType);
        addSelectOneByExampleMethod(interfaze, javaType);
        addUpdateByExampleSelectiveMethod(interfaze);
        addUpdateByExampleWithBLOBsMethod(interfaze);
        addUpdateByExampleWithoutBLOBsMethod(interfaze);
        addUpdateByPrimaryKeySelectiveMethod(interfaze);
        addUpdateByPrimaryKeyWithBLOBsMethod(interfaze);
        addUpdateByPrimaryKeyWithoutBLOBsMethod(interfaze);
        addBatchInsertMethod(interfaze);

        Plugin plugin = context.getPlugins();
        if (plugin.clientGenerated(interfaze, null, introspectedTable)) {
            List<Method> methods = interfaze.getMethods();
            methods.removeIf(method -> Objects.equals(method.getName(), "batchInsert") && Objects.equals(method.getReturnType().getShortName(),
                    "int"));
            interfaze.addImportedTypes(importedTypes);
            GeneratedJavaFile generatedJavaFile = new GeneratedJavaFile(interfaze, targetProject, context.getJavaFormatter());
            generatedJavaFiles.add(generatedJavaFile);
        }
        return generatedJavaFiles;
    }

    private void addBatchInsertMethod(Interface interfaze) {
        initializeAndExecuteListGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                "batchInsert");
    }

    /**
     * addSelectOneByExampleMethod.
     * 2019/6/15
     *
     * @param interfaze              interfaze
     * @param fullyQualifiedJavaType fullyQualifiedJavaType
     * @return void
     * @author machuanpeng
     */
    private void addSelectOneByExampleMethod(Interface interfaze, FullyQualifiedJavaType fullyQualifiedJavaType) {
        if (introspectedTable.getRules().generateSelectByExampleWithoutBLOBs()) {
            initializeAndExecuteExampleGenerator(interfaze, fullyQualifiedJavaType, "selectOneByExample");
        }
    }

    private void addCountByExampleMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateCountByExample()) {
            initializeAndExecuteExampleGenerator(interfaze, new FullyQualifiedJavaType("long"), introspectedTable.getCountByExampleStatementId());
        }
    }

    private void addDeleteByExampleMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateDeleteByExample()) {
            initializeAndExecuteExampleGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getDeleteByExampleStatementId());
        }
    }

    private void addDeleteByPrimaryKeyMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateDeleteByPrimaryKey()) {
            initializeAndExecutePrimaryGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getDeleteByPrimaryKeyStatementId());
        }
    }

    private void addInsertMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateInsert()) {
            initializeAndExecuteModelGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getInsertStatementId());
        }
    }

    private void addInsertSelectiveMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateInsertSelective()) {
            initializeAndExecuteModelGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getInsertSelectiveStatementId());
        }
    }

    private void addSelectByExampleWithBLOBsMethod(Interface interfaze, FullyQualifiedJavaType returnJavaType) {
        if (introspectedTable.getRules().generateSelectByExampleWithBLOBs()) {
            initializeAndExecuteExampleGenerator(interfaze, returnJavaType, introspectedTable.getSelectByExampleWithBLOBsStatementId());
        }
    }

    private void addSelectByExampleWithoutBLOBsMethod(Interface interfaze, FullyQualifiedJavaType returnJavaType) {
        if (introspectedTable.getRules().generateSelectByExampleWithoutBLOBs()) {
            initializeAndExecuteExampleGenerator(interfaze, returnJavaType, introspectedTable.getSelectByExampleStatementId());
        }
    }

    private void addSelectByPrimaryKeyMethod(Interface interfaze, FullyQualifiedJavaType returnJavaType) {
        if (introspectedTable.getRules().generateSelectByPrimaryKey()) {
            initializeAndExecutePrimaryGenerator(interfaze, returnJavaType, introspectedTable.getSelectByPrimaryKeyStatementId());
        }
    }

    private void addUpdateByExampleSelectiveMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateUpdateByExampleSelective()) {
            initializeAndExecuteModelExampleGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getUpdateByExampleSelectiveStatementId());
        }
    }

    private void addUpdateByExampleWithBLOBsMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateUpdateByExampleWithBLOBs()) {
            initializeAndExecuteModelExampleGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getUpdateByExampleWithBLOBsStatementId());
        }
    }

    private void addUpdateByExampleWithoutBLOBsMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateUpdateByExampleWithoutBLOBs()) {
            initializeAndExecuteModelExampleGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getUpdateByExampleStatementId());
        }
    }

    private void addUpdateByPrimaryKeySelectiveMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateUpdateByPrimaryKeySelective()) {
            initializeAndExecuteModelGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getUpdateByPrimaryKeySelectiveStatementId());
        }
    }

    private void addUpdateByPrimaryKeyWithBLOBsMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateUpdateByPrimaryKeyWithBLOBs()) {
            initializeAndExecuteModelGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getUpdateByPrimaryKeyWithBLOBsStatementId());
        }
    }

    private void addUpdateByPrimaryKeyWithoutBLOBsMethod(Interface interfaze) {
        if (introspectedTable.getRules().generateUpdateByPrimaryKeyWithoutBLOBs()) {
            initializeAndExecuteModelGenerator(interfaze, FullyQualifiedJavaType.getBooleanPrimitiveInstance(),
                    introspectedTable.getUpdateByPrimaryKeyStatementId());
        }
    }

    /**
     * 参数为example.
     * 2019/6/14
     *
     * @param interfaze interfaze
     * @return void
     * @author machuanpeng
     */
    private void initializeAndExecuteExampleGenerator(Interface interfaze, FullyQualifiedJavaType fullyQualifiedJavaType, String name) {

        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        FullyQualifiedJavaType type = new FullyQualifiedJavaType(introspectedTable.getExampleType());
        importedTypes.add(type);
        importedTypes.add(fullyQualifiedJavaType);

        Method method = JavaElementGeneratorTools.generateMethod(name, JavaVisibility.DEFAULT, fullyQualifiedJavaType, new Parameter(type, "example"
        ));

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        if (context.getPlugins().clientDeleteByExampleMethodGenerated(
                method, interfaze, introspectedTable)) {
            interfaze.addImportedTypes(importedTypes);
            interfaze.addMethod(method);
        }
    }

    /**
     * 参数为uuid.
     * 2019/6/15
     *
     * @param interfaze              interfaze
     * @param fullyQualifiedJavaType fullyQualifiedJavaType
     * @param name                   name
     * @return void
     * @author machuanpeng
     */
    private void initializeAndExecutePrimaryGenerator(Interface interfaze, FullyQualifiedJavaType fullyQualifiedJavaType, String name) {

        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(fullyQualifiedJavaType);

        Parameter parameter = null;

        if (introspectedTable.getRules().generatePrimaryKeyClass()) {
            FullyQualifiedJavaType type = new FullyQualifiedJavaType(
                    introspectedTable.getPrimaryKeyType());
            importedTypes.add(type);
            parameter = new Parameter(type, "key");
        } else {
            List<IntrospectedColumn> introspectedColumns = introspectedTable
                    .getPrimaryKeyColumns();
            for (IntrospectedColumn introspectedColumn : introspectedColumns) {
                FullyQualifiedJavaType type = introspectedColumn
                        .getFullyQualifiedJavaType();
                importedTypes.add(type);
                parameter = new Parameter(type, introspectedColumn
                        .getJavaProperty());
            }
        }

        Method method = JavaElementGeneratorTools.generateMethod(name, JavaVisibility.DEFAULT, fullyQualifiedJavaType, parameter);

        context.getCommentGenerator().addGeneralMethodComment(method,
                introspectedTable);

        if (context.getPlugins().clientDeleteByPrimaryKeyMethodGenerated(
                method, interfaze, introspectedTable)) {
            interfaze.addImportedTypes(importedTypes);
            interfaze.addMethod(method);
        }
    }

    /**
     * 参数为record.
     * 2019/6/15
     *
     * @param interfaze              interfaze
     * @param fullyQualifiedJavaType fullyQualifiedJavaType
     * @param name                   name
     * @return void
     * @author machuanpeng
     */
    private void initializeAndExecuteModelGenerator(Interface interfaze, FullyQualifiedJavaType fullyQualifiedJavaType, String name) {

        FullyQualifiedJavaType parameterType = introspectedTable.getRules().calculateAllFieldsClass();

        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(fullyQualifiedJavaType);
        importedTypes.add(parameterType);

        Method method = JavaElementGeneratorTools.generateMethod(name, JavaVisibility.DEFAULT, fullyQualifiedJavaType, new Parameter(parameterType,
                "record"));

        context.getCommentGenerator().addGeneralMethodComment(method,
                introspectedTable);

        if (context.getPlugins().clientInsertMethodGenerated(method, interfaze,
                introspectedTable)) {
            interfaze.addImportedTypes(importedTypes);
            interfaze.addMethod(method);
        }
    }

    /**
     * 参数为record、example.
     * 2019/6/15
     *
     * @param interfaze              interfaze
     * @param fullyQualifiedJavaType fullyQualifiedJavaType
     * @param name                   name
     * @return void
     * @author machuanpeng
     */
    private void initializeAndExecuteModelExampleGenerator(Interface interfaze, FullyQualifiedJavaType fullyQualifiedJavaType, String name) {

        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(fullyQualifiedJavaType);

        FullyQualifiedJavaType parameterType = introspectedTable.getRules().calculateAllFieldsClass();
        importedTypes.add(parameterType);

        FullyQualifiedJavaType exampleType = new FullyQualifiedJavaType(introspectedTable.getExampleType());
        importedTypes.add(exampleType);

        Method method = JavaElementGeneratorTools.generateMethod(name, JavaVisibility.DEFAULT, fullyQualifiedJavaType, new Parameter(parameterType,
                "record"), new Parameter(exampleType, "example"));

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        if (context.getPlugins()
                .clientUpdateByExampleSelectiveMethodGenerated(method, interfaze,
                        introspectedTable)) {
            interfaze.addImportedTypes(importedTypes);
            interfaze.addMethod(method);
        }
    }

    /**
     * 参数为List.
     * 2019/6/15
     *
     * @param interfaze              interfaze
     * @param fullyQualifiedJavaType fullyQualifiedJavaType
     * @param name                   name
     * @return void
     * @author machuanpeng
     */
    private void initializeAndExecuteListGenerator(Interface interfaze, FullyQualifiedJavaType fullyQualifiedJavaType, String name) {

        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(fullyQualifiedJavaType);

        FullyQualifiedJavaType listType = FullyQualifiedJavaType.getNewListInstance();
        FullyQualifiedJavaType parameterType = introspectedTable.getRules().calculateAllFieldsClass();
        listType.addTypeArgument(parameterType);
        importedTypes.add(listType);
        importedTypes.add(parameterType);

        Method method = JavaElementGeneratorTools.generateMethod(name, JavaVisibility.DEFAULT, fullyQualifiedJavaType, new Parameter(listType,
                "list"));

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        if (context.getPlugins()
                .clientUpdateByExampleSelectiveMethodGenerated(method, interfaze,
                        introspectedTable)) {
            interfaze.addImportedTypes(importedTypes);
            interfaze.addMethod(method);
        }
    }

}
